/*
 * FreeRTOS V202212.00
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * https://www.FreeRTOS.org
 * https://github.com/FreeRTOS
 *
 */

/******************************************************************************
 * NOTE 1:  This project provides two demo applications.  A simple blinky style
 * project, and a more comprehensive test and demo application.  The
 * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY setting in main.c is used to select
 * between the two.  See the notes on using mainCREATE_SIMPLE_BLINKY_DEMO_ONLY
 * in main.c.  This file implements the simply blinky style version.
 *
 * NOTE 2:  This file only contains the source code that is specific to the
 * basic demo.  Generic functions, such FreeRTOS hook functions, and functions
 * required to configure the hardware, are defined in main.c.
 ******************************************************************************
 *
 * main_blinky() creates one queue, two tasks, and one software timer.  It then
 * starts the scheduler.
 *
 * The Blinky Software Timer:
 * This demonstrates an auto-reload software timer.  The timer callback function
 * does nothing but toggle an LED.
 *
 * The Queue Send Task:
 * The queue send task is implemented by prvQueueSendTask() in main_blinky.c.
 * prvQueueSendTask() repeatedly blocks for 200 milliseconds before sending the
 * value 100 to the queue that was created in main_blinky().
 *
 * The Queue Receive Task:
 * The queue receive task is implemented by prvQueueReceiveTask() in
 * main_blinky.c.  prvQueueReceiveTask() repeatedly blocks on attempts to read
 * from the queue that was created in main_blinky(), toggling an LED each time
 * data is received. The queue send task sends data to the queue every 200
 * milliseconds, so the LED will toggle every 200 milliseconds.
 */

/* Standard includes. */
#include <stdio.h>

/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"

/* Standard demo includes. */
#include "partest.h"
#include "semphr.h"

/* Priorities at which the tasks are created. */
#define mainQUEUE_SEND_TASK_PRIORITY       ( tskIDLE_PRIORITY + 1 )
#define mainQUEUE_RECEIVE_TASK_PRIORITY    ( tskIDLE_PRIORITY + 2 )

/* The rate at which data is sent to the queue.  The 200ms value is converted
 * to ticks using the portTICK_PERIOD_MS constant. */
#define mainQUEUE_SEND_FREQUENCY_MS        ( 200 / portTICK_PERIOD_MS )

/* The number of items the queue can hold.  This is 1 as the receive task
 * will remove items as they are added, meaning the send task should always find
 * the queue empty. */
#define mainQUEUE_LENGTH                   ( 1 )

/* Values passed to the two tasks just to check the task parameter
 * functionality. */
#define mainQUEUE_SEND_PARAMETER           ( 0x1111UL )
#define mainQUEUE_RECEIVE_PARAMETER        ( 0x22UL )

/* The period of the blinky software timer.  The period is specified in ms and
 * converted to ticks using the portTICK_PERIOD_MS constant. */
#define mainBLINKY_TIMER_PERIOD            ( 50 / portTICK_PERIOD_MS )

/* The LED used by the communicating tasks and the blinky timer respectively. */
#define mainTASKS_LED                      ( 0 )
#define mainTIMER_LED                      ( 1 )

/* Misc. */
#define mainDONT_BLOCK                     ( 0 )

/*-----------------------------------------------------------*/

/*
 * The tasks as described in the comments at the top of this file.
 */
static void prvQueueReceiveTask( void * pvParameters );
static void prvQueueSendTask( void * pvParameters );

/*
 * The callback function for the blinky software timer, as described at the top
 * of this file.
 */
static void prvBlinkyTimerCallback( TimerHandle_t xTimer );

/*
 * Called by main() to create the simply blinky style application if
 * mainCREATE_SIMPLE_BLINKY_DEMO_ONLY is set to 1.
 */
void main_blinky( void );

/*-----------------------------------------------------------*/

/* The queue used by both tasks. */
static QueueHandle_t xQueue = NULL;

/*-----------------------------------------------------------*/

void main_blinky( void )
{
    TimerHandle_t xTimer;

    /* Create the queue. */
    xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( unsigned long ) );
    configASSERT( xQueue );

    if( xQueue != NULL )
    {
        /* Create the two tasks as described in the comments at the top of this
         * file. */
        xTaskCreate( prvQueueReceiveTask,                    /* The function that implements the task. */
                     "Rx",                                   /* The text name assigned to the task - for debug only as it is not used by the kernel. */
                     configMINIMAL_STACK_SIZE,               /* The size of the stack to allocate to the task. */
                     ( void * ) mainQUEUE_RECEIVE_PARAMETER, /* The parameter passed to the task - just to check the functionality. */
                     mainQUEUE_RECEIVE_TASK_PRIORITY,        /* The priority assigned to the task. */
                     NULL );                                 /* The task handle is not required, so NULL is passed. */

        xTaskCreate( prvQueueSendTask, "TX", configMINIMAL_STACK_SIZE, ( void * ) mainQUEUE_SEND_PARAMETER, mainQUEUE_SEND_TASK_PRIORITY, NULL );


        /* Create the blinky software timer as described at the top of this file. */
        xTimer = xTimerCreate( "Blinky",                    /* A text name, purely to help debugging. */
                               ( mainBLINKY_TIMER_PERIOD ), /* The timer period. */
                               pdTRUE,                      /* This is an auto-reload timer, so xAutoReload is set to pdTRUE. */
                               ( void * ) 0,                /* The ID is not used, so can be set to anything. */
                               prvBlinkyTimerCallback );    /* The callback function that inspects the status of all the other tasks. */
        configASSERT( xTimer );

        if( xTimer != NULL )
        {
            xTimerStart( xTimer, mainDONT_BLOCK );
        }

        /* Start the tasks and timer running. */
        vTaskStartScheduler();
    }

    /* If all is well, the scheduler will now be running, and the following
     * line will never be reached.  If the following line does execute, then
     * there was insufficient FreeRTOS heap memory available for the idle and/or
     * timer tasks	to be created.  See the memory management section on the
     * FreeRTOS web site for more details. http://www.freertos.org/a00111.html */
    for( ; ; )
    {
    }
}
/*-----------------------------------------------------------*/

static void prvQueueSendTask( void * pvParameters )
{
    TickType_t xNextWakeTime;
    const unsigned long ulValueToSend = 100UL;

    /* Remove compiler warnings in the case that configASSERT() is not dfined. */
    ( void ) pvParameters;

    /* Check the task parameter is as expected. */
    configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_SEND_PARAMETER );

    /* Initialise xNextWakeTime - this only needs to be done once. */
    xNextWakeTime = xTaskGetTickCount();

    for( ; ; )
    {
        /* Place this task in the blocked state until it is time to run again.
         * The block time is specified in ticks, the constant used converts ticks
         * to ms.  While in the Blocked state this task will not consume any CPU
         * time. */
        vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS );

        /* Send to the queue - causing the queue receive task to unblock and
         * toggle the LED.  0 is used as the block time so the sending operation
         * will not block - it shouldn't need to block as the queue should always
         * be empty at this point in the code. */
        xQueueSend( xQueue, &ulValueToSend, 0U );
    }
}
/*-----------------------------------------------------------*/

static void prvQueueReceiveTask( void * pvParameters )
{
    unsigned long ulReceivedValue;

    /* Remove compiler warnings in the case where configASSERT() is not defined. */
    ( void ) pvParameters;

    /* Check the task parameter is as expected. */
    configASSERT( ( ( unsigned long ) pvParameters ) == mainQUEUE_RECEIVE_PARAMETER );

    for( ; ; )
    {
        /* Wait until something arrives in the queue - this task will block
         * indefinitely provided INCLUDE_vTaskSuspend is set to 1 in
         * FreeRTOSConfig.h. */
        xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY );

        /*  To get here something must have been received from the queue, but
         * is it the expected value?  If it is, toggle the LED. */
        if( ulReceivedValue == 100UL )
        {
            vParTestToggleLED( mainTASKS_LED );
            ulReceivedValue = 0U;
        }
    }
}
/*-----------------------------------------------------------*/

static void prvBlinkyTimerCallback( TimerHandle_t xTimer )
{
    /* Avoid compiler warnings. */
    ( void ) xTimer;

    /* This function is called when the blinky software time expires.  All the
     * function does is toggle the LED.  LED mainTIMER_LED should therefore toggle
     * with the period set by mainBLINKY_TIMER_PERIOD. */
    vParTestToggleLED( mainTIMER_LED );
}
/*-----------------------------------------------------------*/
